#include "./crypto.h"
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
#include <vector>

namespace utils {


std::string base64_encode(const uint8_t *buf, size_t len) {

    BIO *bio, *b64;
    BUF_MEM *buf_ptr;
    
    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);

    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
    BIO_write(bio, buf, len);
    BIO_flush(bio);
    BIO_get_mem_ptr(bio, &buf_ptr);

    std::string b64_text(buf_ptr->data, buf_ptr->length);

    BIO_free_all(bio);
    return b64_text;
}

static std::vector<uint8_t> encrypt_aes_ecb(const uint8_t *key, const uint8_t *src, size_t src_len) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();

    EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL);
    EVP_CIPHER_CTX_set_padding(ctx, 0);
    
    size_t count = src_len / 16;
    size_t next = src_len % 16;
    size_t len = src_len + 16 + ((16 - next) % 16);

    std::vector<uint8_t> dst(len, 0);
    int out_len = 0;
    uint8_t *out = (uint8_t *) dst.data();
    for (size_t i = 0; i < count; i ++) {
        EVP_EncryptUpdate(ctx, out, &out_len,  src, 16);
        src += 16;
        out += 16;
    }
    if (next != 0) {
        uint8_t buf[16];
        memset(buf, 0, 16);
        memcpy(buf, src, next);
        EVP_EncryptUpdate(ctx, out, &out_len, buf, 16);
    }
    uint8_t buf[16];
    memset(buf, 0, 16);
    buf[0] = 16 + ((16 - next) % 16);
    EVP_EncryptUpdate(ctx, out, &out_len, buf, 16);
    EVP_CIPHER_CTX_free(ctx);

    return dst;
}

std::string encrypt_aes_base64(const uint8_t *key, const uint8_t *src, size_t src_len) {
    std::vector<uint8_t> buf = encrypt_aes_ecb(key, src, src_len);
    return base64_encode(buf.data(), buf.size());
}


}







